package cn.demoxy.component;

import cn.demoxy.enums.ExceptionMessageEnum;
import cn.demoxy.exception.DemoException;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.Item;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import cn.demoxy.properties.MinioProperties;

/**
 * @auther demo
 * @date 2022/5/26 9:27
 * minio操作类
 */
@Component
@AllArgsConstructor
public class MinioUtils {

    private MinioClient minioClient;

    private MinioProperties minioProperties;

    public String formatFileSize(long fileS) {
        DecimalFormat df = new DecimalFormat("#.00");
        String fileSizeString = "";
        String wrongSize = "0B";
        if (fileS == 0) {
            return wrongSize;
        }
        if (fileS < 1024) {
            fileSizeString = df.format((double) fileS) + " B";
        } else if (fileS < 1048576) {
            fileSizeString = df.format((double) fileS / 1024) + " KB";
        } else if (fileS < 1073741824) {
            fileSizeString = df.format((double) fileS / 1048576) + " MB";
        } else {
            fileSizeString = df.format((double) fileS / 1073741824) + " GB";
        }
        return fileSizeString;
    }

    /**
     * 创建桶
     *
     * @param bucketName 桶名称
     */
    @SneakyThrows
    public void createBucket(String bucketName) {
        boolean found =
                minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        if (!found) {
            minioClient.makeBucket(
                    MakeBucketArgs.builder()
                            .bucket(bucketName)
                            .region("cn-beijing")
                            .build());
        }
    }

    /**
     * 删除桶
     */
    @SneakyThrows
    public void removeBucket() {
        minioClient.removeBucket(RemoveBucketArgs.builder().bucket(minioProperties.getBucketName()).build());
    }


    /**
     * 上传文件
     *
     * @param objectName 文件名
     * @param stream     流
     * @param fileSize   文件大小
     * @param type       文件类型
     * @throws Exception
     */
    public void putObject(String objectName, InputStream stream, Long fileSize, String type) throws Exception {
        minioClient.putObject(
                PutObjectArgs.builder().bucket(minioProperties.getBucketName()).object(objectName).stream(
                        stream, fileSize, -1)
                        .contentType(type)
                        .build());
    }

    public void removeObject(String objectName) throws Exception {
        minioClient.removeObject(
                RemoveObjectArgs.builder().bucket(minioProperties.getBucketName()).object(objectName)
                        .build());


    }

    /**
     * 判断文件夹是否存在
     *
     * @param prefix 文件夹名字
     * @return
     */
    @SneakyThrows
    public Boolean folderExists(String prefix) {
        Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(minioProperties.getBucketName())
                .prefix(prefix).recursive(false).build());
        for (Result<Item> result : results) {
            Item item = result.get();
            if (item.isDir()) {
                return true;
            }
        }
        return false;
    }

    /**
     * 创建文件夹
     *
     * @param path 路径
     */
    @SneakyThrows
    public void createFolder(String path) {
        minioClient.putObject(PutObjectArgs.builder().bucket(minioProperties.getBucketName()).object(path)
                .stream(new ByteArrayInputStream(new byte[]{}), 0, -1).build());
    }

    /**
     * 获取文件在minio在服务器上的外链
     *
     * @param objectName 文件名
     * @return
     */
    @SneakyThrows
    public String getObjectUrl(String objectName) {
        return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                        .method(Method.GET)
                        .bucket(minioProperties.getBucketName())
                        .object(objectName)
                        .build());
    }

    /**
     * 文件上传
     *
     * @param file 要上传的文件
     * @return
     */
    public String upload(MultipartFile[] file) {
        if (file == null || file.length == 0) {
            return "上传文件不能为空";
        }
        List<String> filenameList = new ArrayList<>(file.length);

        for (MultipartFile multipartFile : file) {
            String originalFilename = multipartFile.getOriginalFilename();
            filenameList.add(originalFilename);
            try {
                InputStream in = multipartFile.getInputStream();
                minioClient.putObject(PutObjectArgs.builder()
                        .bucket(minioProperties.getBucketName())
                        .object(originalFilename)
                        .stream(in, multipartFile.getSize(), -1)
                        .contentType(multipartFile.getContentType())
                        .build());
                in.close();

            } catch (Exception e) {
                return "上传失败";
            }
        }
        return "上传成功";
    }

    /**
     * 文件上传单个文件
     *
     * @param file 要上传的文件
     * @return
     */
    public String uploadOne(MultipartFile file) {
        if (file == null) {
            throw new DemoException(ExceptionMessageEnum.UPLOAD_FILE_IS_NOT_NULL.getCode(), ExceptionMessageEnum.UPLOAD_FILE_IS_NOT_NULL.getMsg());
        }
        String suffixName = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".") + 1);
        String fileName = UUID.randomUUID().toString() + "." + suffixName;
        try {
            InputStream in = file.getInputStream();
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(minioProperties.getBucketName())
                    .object(fileName)
                    .stream(in, file.getSize(), -1)
                    .contentType(file.getContentType())
                    .build());
            in.close();

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return fileName;
    }

    /**
     * 文件下载
     *
     * @param response
     * @param fileName
     */
    public void download(HttpServletResponse response, String fileName) {
        InputStream in = null;
        try {
            StatObjectResponse stat = minioClient.statObject(
                    StatObjectArgs.builder()
                            .bucket(minioProperties.getBucketName())
                            .object(fileName)
                            .build()
            );
            response.setContentType("application/force-download");
            // response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            in = minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(minioProperties.getBucketName())
                            .object(fileName)
                            .build()
            );
            IOUtils.copy(in, response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    /**
     * 判断文件大小
     *
     * @param len  文件长度
     * @param size 限制大小
     * @param unit 限制单位（B,K,M,G）
     * @return
     */
    public boolean checkFileSize(Long len, int size, String unit) {
        double fileSize = 0;
        if ("B".equals(unit.toUpperCase())) {
            fileSize = (double) len;
        } else if ("K".equals(unit.toUpperCase())) {
            fileSize = (double) len / 1024;
        } else if ("M".equals(unit.toUpperCase())) {
            fileSize = (double) len / 1048576;
        } else if ("G".equals(unit.toUpperCase())) {
            fileSize = (double) len / 1073741824;
        }
        if (fileSize > size) {
            return false;
        }
        return true;
    }
}
